package cn.bouncyslime.graduationProject.nba.crawler.spider;

import cn.bouncyslime.graduationProject.nba.dto.PlayerInfoDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Request;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.pipeline.ConsolePipeline;
import us.codecraft.webmagic.processor.PageProcessor;
import us.codecraft.webmagic.selector.Html;
import us.codecraft.webmagic.selector.Selectable;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author 张一卓
 * @date 2020/4/1 1:03
 **/
public class PlayerInfoPageProcessor implements PageProcessor {

    private static final Logger logger = LoggerFactory.getLogger(NewTeamPageProcessor.class);

    private static final String BASE_URL = "http://www.stat-nba.com";

    private static final String START_URL = "http://www.stat-nba.com/playerList.php";

    private static final String PLAYER_URL = "http://www.stat-nba.com/player";

    /**
     * 球员链接匹配规则
     */
    private static String regex = "\\.(/([a-zA-Z]+)/(\\d+).([a-zA-Z]+))";

    private static String heightRegex = "([\\d\\.]+)米\\((\\d+尺\\d+寸)\\)";

    private static String weightRegex = "([\\d]+)公斤\\(([\\d]+)磅\\)";

    private static String salaryRegex = "(\\d+)(\\S+)";

    private static Pattern pattern = Pattern.compile(regex);

    private static Pattern heightPattern = Pattern.compile(heightRegex);

    private static Pattern weightPattern = Pattern.compile(weightRegex);

    private static Pattern salaryPattern = Pattern.compile(salaryRegex);

    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日");

    private Site site = Site.me().setSleepTime(1500).setRetryTimes(3).setRetrySleepTime(1000).setCharset("utf-8")
            .setUserAgent("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.369")
            .addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9")
            .addHeader("Connection", "keep-alive")
            .addHeader("Host", "www.stat-nba.com")
            .addCookie("__cfduid", "def62db9852e6b626f3d744bacadd235a1585654597")
            .addCookie("Hm_lvt_102e5c22af038a553a8610096bcc8bd4", "1585654597")
            .addCookie("yjs_id", "aHR0cHM6Ly93d3cuYmFpZHUuY29tL2xpbms/dXJsPXlGVU92Y0NMMERvR3ZUT01SVm1kS05NeExSeDNEWHFxcklfakxDSXVpMHJxY1E0RkJITTFncEhGX2lQcHJ2ekgmd2Q9JmVxaWQ9ZGM3OTgyN2YwMDczMjg1OTAwMDAwMDAzNWU4MzJiNDN8MTU4NTY1NDU5Njg0Ng")
            .addCookie("ctrl_time", "1")
            .addCookie("Hm_lpvt_102e5c22af038a553a8610096bcc8bd4", "1585672286");

    @Override
    public void process(Page page) {
        String pageUrl = page.getUrl().toString();
        if (page.isDownloadSuccess()) {
            Html html = page.getHtml();
            // 当前爬取页面是 球员列表 页面时
            if (pageUrl.contains(START_URL)) {
                logger.info(">> 正在处理 球队 列表页面：" + pageUrl);
                Selectable selectable = html.xpath("div[@class='playerList']");
                List<String> playerUrlList;
                // 匹配到就是说该页面有球员数据
                if (selectable.match()) {
                    int nodeIndex = 0;
                    if (selectable.nodes().size() == 2) {
                        nodeIndex = 1;
                    }
                    playerUrlList = selectable.nodes().get(nodeIndex).xpath("div[@class='name']/a/@href").all();
                    for (String playerSuffixUrl : playerUrlList) {
                        Matcher matcher = pattern.matcher(playerSuffixUrl);
                        if (matcher.find()) {
                            String url = BASE_URL + matcher.group(1);
                            Request request = new Request(url);
                            request.putExtra("playerId", matcher.group(3));
                            page.addTargetRequest(request);
                        }
                    }
                }
                // 当爬取完 Z 的页面之后就不再爬取
                char endChar = pageUrl.charAt(pageUrl.length() - 1);
                if (endChar <= 90) {
                    page.addTargetRequest(pageUrl.substring(0, pageUrl.length() - 1) + (char) (endChar + 1));
                }
            } else if (pageUrl.contains(PLAYER_URL)) {
                logger.info(">> 正在处理 球员 详细信息页面：" + pageUrl);
                PlayerInfoDto playerInfoDto = new PlayerInfoDto();
                // 解析球员id
                playerInfoDto.setPlayerId(Integer.valueOf(page.getRequest().getExtra("playerId").toString()));
                // 解析出球员中文名和英文名
                Selectable selectable = html.xpath("div[@class='playerinfo']");
                String playerName = selectable.xpath("div[@class='name']/text()").get();
                String[] playerNames = playerName.split("/");
                // 有的人是没有中文名字的
                if (playerNames.length == 2) {
                    playerInfoDto.setPlayerCName(playerNames[0]);
                    playerInfoDto.setPlayerEName(playerNames[1]);
                } else {
                    playerInfoDto.setPlayerEName(playerNames[0]);
                }
                // 解析球员详细信息内容
                List<String> playerDetail = selectable.xpath("div[@class='detail']/div[@class='row'][@id!='numberDetail']/text()").all();
                // 解析球员详细信息标签
                List<String> playerInfoKeys = selectable.xpath("div[@class='detail']/div[@class='row'][@id!='numberDetail']/div[@class='column']/text()").all();
                // 标签中含有 全角空格，进行替换
                List<String> playerInfoKeyList = new ArrayList<>();
                playerInfoKeys.forEach(x -> playerInfoKeyList.add(x.replace((char) 12288, ' ').replaceAll("[:\\s]+", "")));
                for (int i = 0; i < playerInfoKeyList.size(); i++) {
                    switch (playerInfoKeyList.get(i)) {
                        case "位置":
                            playerInfoDto.setPlayerRole(playerDetail.get(i));
                            break;
                        case "身高":
                            String heights = playerDetail.get(i);
                            Matcher matcher = heightPattern.matcher(heights);
                            if (matcher.find()) {
                                playerInfoDto.setHeightMetric(new BigDecimal(matcher.group(1)));
                                playerInfoDto.setHeightBritish(matcher.group(2));
                            }
                            break;
                        case "体重":
                            String weights = playerDetail.get(i);
                            matcher = weightPattern.matcher(weights);
                            if (matcher.find()) {
                                playerInfoDto.setWeightKilogram(Integer.valueOf(matcher.group(1)));
                                playerInfoDto.setWeightPound(Integer.valueOf(matcher.group(2)));
                            }
                            break;
                        case "出生日期":
                            try {
                                playerInfoDto.setBirthday(dateFormat.parse(playerDetail.get(i)));
                            } catch (ParseException e) {
                                e.printStackTrace();
                            }
                            break;
                        case "出生城市":
                            playerInfoDto.setBirthplace(playerDetail.get(i));
                            break;
                        case "高中":
                            playerInfoDto.setSeniorHighSchool(playerDetail.get(i));
                            break;
                        case "大学":
                            playerInfoDto.setUniversity(playerDetail.get(i));
                            break;
                        case "球衣号码":
                            Selectable numberSelectable = selectable.xpath("div[@class='detail']/div[@class='row'][@id!='numberDetail']").nodes().get(i);
                            Selectable teamSelectable = numberSelectable.xpath("a[@target='_blank']/text()");
                            if (teamSelectable.match()) {
                                List<String> teams = teamSelectable.all();
                                String[] strings = playerDetail.get(i).split("退役");
                                StringBuilder uniformNumber = new StringBuilder();
                                for (int j = 0; j < teams.size(); j++) {
                                    uniformNumber.append(strings[j]);
                                    uniformNumber.append(teams.get(j));
                                    uniformNumber.append("退役");
                                }
                                playerInfoDto.setUniformNumber(uniformNumber.toString().replace(" ", ""));
                            } else {
                                playerInfoDto.setUniformNumber(playerDetail.get(i).replace(" ", ""));
                            }
                            break;
                        case "选秀情况":
                            Selectable draftSelectable = selectable.xpath("div[@class='detail']/div[@class='row'][@id!='numberDetail']").nodes().get(i);
                            String playerSelectedTeam;
                            if (draftSelectable.xpath("a/text()").match()) {
                                playerSelectedTeam = draftSelectable.xpath("a/text()").get();
                                playerInfoDto.setDraft((playerDetail.get(i).replace("选中", playerSelectedTeam) + "选中").replace(" ", ""));
                            } else {
                                playerInfoDto.setDraft(playerDetail.get(i));
                            }
                            break;
                        case "当前薪金":
                            String salary = playerDetail.get(i);
                            matcher = salaryPattern.matcher(salary);
                            if (matcher.find()) {
                                playerInfoDto.setSalary(Integer.valueOf(matcher.group(1)));
                            }
                            break;
                    }
                }
                page.putField("player", playerInfoDto);
            }
        } else {
            logger.warn(">> " + pageUrl + "页面爬取失败");
        }
    }

    @Override
    public Site getSite() {
        return site;
    }

    public static void main(String[] args) {
        Spider.create(new PlayerInfoPageProcessor()).addUrl("http://www.stat-nba.com/playerList.php?il=0&lil=A")
                .addPipeline(new ConsolePipeline())
                .thread(5).run();
    }
}
